﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Eco.Handles;
using segDroopyEyes.Eco.Services.XmlQueryService2;
using CommandEditor.Properties;
using System.IO;
using uCoreInterfaces2;
using System.Windows.Forms;
using CommandLists.Model;
using Eco.Services;
using Descriptions_DomainModel.Model;
using CommonClasses.Utility;

namespace CommandEditor
{
    /// <summary>
    /// Класс, реализующий загрузку и сохранение различных файлов для списков команд
    /// </summary>
    public class CommandListDataLoader
    {
        private EcoSpace ecoSpace;

        public CommandListDataLoader(EcoSpace ecoSpace)
        {
            this.ecoSpace = ecoSpace;
        }

        /// <summary>
        /// Загрузить конфиг для СК из файла
        /// </summary>
        /// <param name="fileName">Имя файла</param>
        public void LoadConfigFromFile(string fileName)
        {
            var dess = ecoSpace.GetEcoService<IXmlSerializerService>();

            //TODO: открытие из 2х файлов с использованием ID (в этом случае не будет дублирования)

            //сейчас - открытие и ручками бегаем по объектам, удаляем лишние и налаживаем связи

            var compath = Path.ChangeExtension(
                Path.GetDirectoryName(Path.GetFullPath(fileName))
                + Path.DirectorySeparatorChar
                + Path.GetFileNameWithoutExtension(fileName),
                Settings.Default.CommandsExtension);


            //проверяем наличие обоих файлок конфига для СК
            //наличие главного файла проверяется в диалоге открытия
            if (!File.Exists(compath))
            {
                MessageBox.Show("Нет файла настроек команд!\n"
                    + Path.GetFileName(compath),
                    "Отсутствует файл!",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);

                return;
            }

            PathUtility.RestoreCurrentDirectory(Locality.ExeFile);

            dess.LoadFromFile(fileName);

            dess.LoadFromFile(compath);

            ConnectCAndCL();

            //ConnectCLAndN();

            //ClearInvalidFuncReferences();

            //Так реализуется открытие файла с помощью стандартных средств EcoSpace -
            // - PersistenceProviderXml

            //CommandListsDGV.Visible = false;
            //EcoSpace.Active = false;
            //EcoSpace.FileName = path;
            //EcoSpace.Active = true;
            //CommandListsDGV.Visible = true;
        }

        /// <summary>
        /// Сохранить конфиг для СК в файл
        /// </summary>
        /// <param name="fileName">Имя файла</param>
        public void SaveConfigToFile(string fileName)
        {
            var sserv = ecoSpace.GetEcoService<IXmlQueryServiceSEG>();

            ////сохраняем всё в один файл - плохо, т.к. при открытии файла некоторые команды будут продублированы

            //sserv.ExeXmlFile2XmlFile(null,
            //    Settings.Default.SaveAllQuery,
            //    "", Path.ChangeExtension(EcoSpace.FileName, Settings.Default.MainExtension));

            var compath = Path.GetDirectoryName(fileName) +
                Path.DirectorySeparatorChar +
                Path.GetFileNameWithoutExtension(fileName) + "." + 
                Settings.Default.CommandsExtension;

            //сохраняем списки команд
            PathUtility.RestoreCurrentDirectory(Locality.ExeFile);
            sserv.ExeXmlFile2XmlFile(null,Settings.Default.SaveCLQuery,"", fileName);

            //сохраняем команды
            PathUtility.RestoreCurrentDirectory(Locality.ExeFile);
            sserv.ExeXmlFile2XmlFile(null,Settings.Default.SaveCSQuery,"", compath);
        }

        /// <summary>
        /// Сохранить в файл программу СК
        /// </summary>
        /// <param name="fileName">Имя файла</param>
        public void SaveProgramToFile(string fileName)
        {
            var sserv = ecoSpace.GetEcoService<IXmlQueryServiceSEG>();

            //string saveProgramQuery = Settings.Default.SaveProgramQuery;
            //saveProgramQuery = Path.GetFullPath(saveProgramQuery);

            PathUtility.RestoreCurrentDirectory(Locality.ExeFile);

            sserv.ExeXmlFile2XmlFile(null, Settings.Default.SaveProgramQuery,
                "", Path.ChangeExtension(fileName, Settings.Default.ProgrammExtension));
        }

        /// <summary>
        /// Загрузить программу для СК из файла
        /// </summary>
        /// <param name="fileName">Имя файла с программой</param>
        public void LoadProgramFromFile(string fileName)
        {
            var ocl = ecoSpace.GetEcoService<IOclService>();
            var dess = ecoSpace.GetEcoService<IXmlSerializerService>();

            PathUtility.RestoreCurrentDirectory(Locality.ExeFile);

            var oldLists = ocl.Evaluate("CommandList.allInstances").GetAsIList<CommandList>();

            dess.LoadFromFile(fileName);

            ocl.Evaluate("CommandList.allInstances").GetAsIList<CommandList>()
                .ForEach(list => list.Initialized = false);

            try
            {
                ConnectCLAndPr();
            }
            catch (InvalidDataException dataex)
            {
/*                var listsForDelete = ocl.Evaluate("CommandList.allInstances")
                    .GetAsIList<CommandList>()
                    .Where(comlist => !oldLists.Contains(comlist));

                var listsForDeleteCopy = new List<CommandList>(listsForDelete);

                var count = listsForDeleteCopy.Count;
                for (int i = 0; i < count; i++)
                {
                    var commandsCount = listsForDeleteCopy[0].CommandSequence.Count;
                    for (int j = 0; j < commandsCount; j++)
                        listsForDeleteCopy[i].CommandSequence[0].AsIObject().Delete();

                    if (!listsForDeleteCopy[i].AsIObject().Deleted)
                        listsForDeleteCopy[i].AsIObject().Delete();
                }*/
                throw;
            }
            finally
            {
                GC.Collect();
            }

            ocl.Evaluate("CommandList.allInstances").GetAsIList<CommandList>()
                .ForEach(list => list.Initialized = true);
        }

        /// <summary>
        /// Восстановить связи CommandList.CommandsScope у всех списков команд
        /// </summary>
        private void ConnectCAndCL()
        {
            var ocl = ecoSpace.GetEcoService<IOclService>();
            var commands = ocl.Evaluate("Command.allInstances").GetAsIList<Command>();
            var comlists = ocl.Evaluate("CommandList.allInstances").GetAsIList<CommandList>();

            //в каждом СК сразу после десериализации существуют ссылки только на пустые команды, но с уникальными OldID
            foreach (var comlist in comlists)
            {
                //выбираем "левые" команды
                var fakescope = from com in comlist.CommandsScope
                                where com.Name == string.Empty
                                select com;
                foreach (var com in fakescope)
                {
                    //Ищем замену из нормальных команд
                    var normal = commands.Find(c => (c.OldID == com.OldID) && (c.Name != string.Empty));

                    //добавляем нормальную
                    comlist.CommandsScope.Add(normal);
                }

                //удаляем "левые команды"
                int count = fakescope.Count();
                for (int i = 0; i < count; i++)
                    if ((!fakescope.ElementAt(0).AsIObject().Deleted) && (fakescope.ElementAt(0).Name == string.Empty))
                        fakescope.ElementAt(0).AsIObject().Delete();
            }
        }

        /// <summary>
        /// Восстановить связь CommandList.Nodes
        /// </summary>
        private void ConnectCLAndN()
        {
            var ocl = ecoSpace.GetEcoService<IOclService>();
            var nodes = ocl.Evaluate("Node.allInstances").GetAsIList<Node>();
            var comlists = ocl.Evaluate("CommandList.allInstances").GetAsIList<CommandList>();
            //если не загружено узлов, то и восстанавливать никак
            if (nodes.Count == 0)
                return;

            //в каждом СК сразу после десериализации существуют ссылки только на пустые команды, но с уникальными OldID
            foreach (var comlist in comlists)
            {
                //выбираем "левые" команды
                var fakenodes = from nod in comlist.Nodes
                                where nod.Name == string.Empty
                                select nod;
                foreach (var com in fakenodes)
                {
                    //Ищем замену из нормальных узлов
                    var normal = nodes.Find(n => (n.OldID == com.OldID) && (n.Name != string.Empty));

                    //добавляем нормальную
                    comlist.Nodes.Add(normal);
                }

                //удаляем "левые команды"
                int count = fakenodes.Count();
                for (int i = 0; i < count; i++)
                    if ((!fakenodes.ElementAt(0).AsIObject().Deleted) && (fakenodes.ElementAt(0).Name == string.Empty))
                        fakenodes.ElementAt(0).AsIObject().Delete();
            }
        }

        /// <summary>
        /// Восстановить связь CommandList.CommandSequence
        /// </summary>
        private void ConnectCLAndPr()
        {
            var ocl = ecoSpace.GetEcoService<IOclService>();
            var comlists = ocl.Evaluate("CommandList.allInstances").GetAsIList<CommandList>();

            //отбираем только что загруженные СК
            var loaded = from cl in comlists
                         group cl by cl.Name into samename
                         select samename;
            try
            {
                if (!loaded.All(samen => samen.Count() == 2))
                    throw new InvalidDataException("Program file doesn't corresponds to loaded config!");

                //заменяем последовательности команд в СК из конфига на только что загруженные
                foreach (var samename in loaded)
                {
                    var normal = samename.FirstOrDefault(list => list.ImagePath != string.Empty);
                    var loadedcl = samename.FirstOrDefault(list => list.ImagePath == string.Empty);

                    if ((loadedcl == null) || (normal == null))
                        throw new InvalidDataException("Program file doesn't corresponds to loaded config!");

                    // нужно удалить сами все команды из этого списка
                    while (normal.CommandSequence.Count != 0)
                    {
                        var loadedCommand = normal.CommandSequence[0];
                        normal.CommandSequence.RemoveAt(0);
                        loadedCommand.AsIObject().Delete();
                    }
                    normal.CommandSequence.Clear();

                    for (int i = 0; i < loadedcl.CommandSequence.Count; i++)
                        normal.InsertTact(i, loadedcl.CommandSequence[i]);

                    //loadedcl.CommandSequence.Clear();
                    //loadedcl.AsIObject().Delete();

                    loadedcl.CommandSequence.ForEach((com) => { });
                }
            }
            catch (InvalidDataException dataex)
            {
                throw;
            }
            finally
            {
                //Удаляем все СК, только что загруженные
                int count = loaded.Count();
                for (int i = 0; i < count; i++)
                {
                    var comlist = (loaded.ElementAt(i).FirstOrDefault(list => list.ImagePath == string.Empty) as CommandList);
                    if (comlist != null)
                    {
                        // нужно удалить сами все команды из этого списка
                        while (comlist.CommandSequence.Count != 0)
                        {
                            var loadedCommand = comlist.CommandSequence[0];
                            comlist.CommandSequence.RemoveAt(0);
                            while (loadedCommand.Parameters.Count != 0)
                            {
                                var deletingParameter = loadedCommand.Parameters[0];
                                loadedCommand.Parameters.RemoveAt(0);
                                deletingParameter.AsIObject().Delete();
                            }
                            loadedCommand.Parameters.Clear();
                            loadedCommand.AsIObject().Delete();
                        }

                        // нужно удалить сами все команды из этого списка
                        while (comlist.CommandsScope.Count != 0)
                        {
                            var loadedCommand = comlist.CommandsScope[0];
                            comlist.CommandsScope.RemoveAt(0);
                            while (loadedCommand.Parameters.Count != 0)
                            {
                                var deletingParameter = loadedCommand.Parameters[0];
                                loadedCommand.Parameters.RemoveAt(0);
                                deletingParameter.AsIObject().Delete();
                            }
                            loadedCommand.Parameters.Clear();
                            loadedCommand.AsIObject().Delete();
                        }

                        // ну и сам список удаляем
                        comlist.CommandSequence.Clear();
                        comlist.CommandsScope.Clear();
                        comlist.AsIObject().Delete();
                    }
                }
                GC.Collect();
            }
        }

        private void ClearInvalidFuncReferences()
        {
            var ocl = ecoSpace.GetEcoService<IOclService>();
            var functions = ocl.Evaluate("DomainModel.allInstances->first.FunctionLibrary.Functions").GetAsIList<Function>();
            var commands = ocl.Evaluate("Command.allInstances").GetAsIList<Command>();

            foreach (var com in commands)
            {
                if (functions.Find(func => func.Name == com.FunctionName) == null)
                    com.AsIObject().Delete();
            }
        }
    }
}
